home *** CD-ROM | disk | FTP | other *** search
/ Nebula 2 / Nebula Two.iso / SourceCode / MiscKit1.7.1 / MiscKit / Examples / cnvwrap / cnvwrap.m next >
Encoding:
Text File  |  1995-07-09  |  19.6 KB  |  508 lines

  1. //
  2. //    cnvwrap.m 
  3. //         Program for automatically generating degenerate (convenience)
  4. //         methods for methods with optional parameters
  5. //
  6. //                       Written by Carl Lindberg
  7. //               Copyright (c) 1994,1995 by Carl Lindberg.
  8. //                  Version 1.2  All rights reserved.
  9. //        This notice may not be removed from this source code.
  10. //
  11. //    This object is included in the MiscKit by permission from the author
  12. //    and its use is governed by the MiscKit license, found in the file
  13. //    "LICENSE.rtf" in the MiscKit distribution.  Please refer to that file
  14. //    for a list of all applicable permissions and restrictions.
  15. //
  16. //
  17.  
  18. //    This program takes method declarations - with default values attached
  19. //   to optional parameters - and outputs all possible degenerate methods
  20. //   along with their header lines.
  21. //
  22. //
  23. //   Command-line options:
  24. //
  25. //       -i filename     Specify input file.  Reads from stdin if this
  26. //                       option not used.
  27. //       -m filename     Output method implementations to named file.  Adds
  28. //                       '.m' to filename if not there already.  Outputs to
  29. //                       stdout if this option is not used.
  30. //       -h filename     Output method headers to named file.  Adds '.h' to
  31. //                       filename if not there already.  Outputs to stdout if
  32. //                       this option is not used.
  33. //       -f filename     Just like -h filename.h -m filename.m.  Automatically
  34. //                       adds .h and .m extensions to filename, and outputs
  35. //                       headers and methods, respectively, to the two files.
  36. //       -e {h|m|hm}     Excludes the {header|implementation|both} of the full
  37. //                       method from being output.  For example, -em will print
  38. //                       out all headers, but will not print the implementation
  39. //                       of the full method (normally, it is printed with a
  40. //                       blank body).
  41. //       -a              Appends to output files instead of overwriting.
  42. //       -H              Help
  43. //
  44. //
  45. //
  46. //   SYNTAX FOR INPUT LINE(S):
  47. //
  48. //    Input lines are basically the same as header lines, except that
  49. //    default values to parameters are put inside square brackets and
  50. //    tacked directly onto the end of that parameters.  Also, no
  51. //    semicolon at the end.
  52. //
  53. //
  54. //    1) Leave at least one space/tab after the "-" or "+".
  55. //    2) ONE line per method - don't break up a long method into 2 or more
  56. //         lines.
  57. //    3) Default values signify an optional parameter.  These should be 
  58. //         between square brackets tacked on the end of a parameter -
  59. //         NO SPACES before the open bracket.
  60. //       The 'default value' may contain any character - including whitespaces -
  61. //         EXCEPT a right bracket character.  Even then, you are allowed
  62. //         to have right bracket character(s) so long as no whitespaces come
  63. //         _after_ it.  Ex:
  64. //
  65. //   Legal:     with:(char *)[[sender stringValue]]
  66. //              with:(char *)[[[sender selectedCell]stringValue]]
  67. //   Illegal:   with:(char *)[[sender stringValue] ]
  68. //              with:(char *)[[[sender selectedCell] stringValue]]
  69. //
  70. //       This was as flexible as I could figure out - it's a limitation
  71. //         of the regular expression used to search.  If this still isn't
  72. //         enough, than you could simply put in a word like FOOBAR as the
  73. //         default value, and then search-and-replace FOOBAR with the real
  74. //         default value on the output.
  75. //
  76. //    4) Every variable must be typecast - even if it's an id, please put
  77. //         (id) as the typecast.  A method return type is optional, though.
  78. //    5) If the first parameter - really the method name - is optional,
  79. //         then *every* parameter must be optional too, or else you will
  80. //         get duplicate methods output.
  81. //
  82. //
  83. //   The program works by first parsing each line, and splitting it
  84. //  up into four lists, each list containing some needed portion of
  85. //  each parameter.  Then the program goes into a second main loop
  86. //  which works up the needed text using the values in the lists.
  87. //
  88. //  For the input:
  89. //
  90. //  - (int)spotOfStr:(const char *)str occurrenceNum:(int)n[0] caseSensitive:(BOOL)sense[YES]
  91. //
  92. //  the four lists would end up so:
  93. //
  94. //
  95. //            paramtypeList                      paramList         paramvalList   defaultList
  96. //            (whole param minus any             (param name       (variable      (default 
  97. //              default value)                     only)             name)           name)
  98. //            --------------------------------   --------------    ------------   -----------
  99. //  Param 1:  (int)spotOfStr:(const char *)str   spotOfStr:        str            [blank]
  100. //  Param 2:  occurrenceNum:(int)n               occurrenceNum:    n              0
  101. //  Param 3:  caseSensitive:(BOOL)sense          caseSensitive:    sense          YES
  102. //
  103. //
  104. //   There should be (number of optional parameters)**2 possible method
  105. //  combinations.  The exception is that if the first "parameter"
  106. //  (spotOfStr in the example above) is optional, only one more possible
  107. //  method is added -- the one where every parameter is left out.  You
  108. //  can't leave the first parameter out and include others, obviously.
  109. //  Therefore, if the first parameter is optional, *every* parameter should
  110. //  be optional -- else duplicate methods will be output.
  111. //
  112. //
  113. //
  114. //
  115. //   You may thank the piles of convenience methods in MiscString for 
  116. //  prompting me to write this program. :-)  Enjoy.
  117. //
  118. //  Version 1.1  June 7, 1995
  119. //    - fixed bug where having '(' or '[' in the default value section would
  120. //         confuse program
  121. //    - added -e {h|m|hm} switch to exclude outputting of the last possible
  122. //         header or method.  This lets you code the full method first, then
  123. //         just output the headers/code for the convenience methods only.
  124. //  Version 1.2  July 8, 1995
  125. //    - changed the regular expression search to allow more possibilities of
  126. //         default section values.
  127.  
  128. #include <appkit/appkit.h>
  129. #include <misckit/MiscString.h>
  130.  
  131.   void dohelp();
  132.   void usage();
  133.   void freestuff();
  134.  
  135. // These are needed to be declared outside main() for freestuff()
  136. id mainstr, headstring, specstring, hName, mName, iName, returnstr, mfilestr,
  137.     hfilestr, defaultList, paramList, paramtypeList, paramvalList;
  138.  
  139. void main(int argc, char **argv)
  140. {
  141.   char ch;
  142.  
  143.   BOOL isOptArray[50];                    //ith slot YES if param #i is optional
  144.   BOOL append = NO;                        //append to given filenames, or overwrite?
  145.   BOOL outputFinalHeader = YES;
  146.   BOOL outputFinalMethod = YES;
  147.   int i, j, numposs, numparam, thenum, len, spot2, spot3, startposs;
  148.  
  149.   FILE *infile, *moutfile, *houtfile;
  150.  
  151.   id tmp;
  152.  
  153.   // Initialize some stuff
  154.   mainstr   = [MiscString new];
  155.   specstring= [MiscString new];        //special; used if first param is optional
  156.   headstring= [MiscString new];        //for building up the header line
  157.   hfilestr  = [MiscString new];        //for storing method headers
  158.   mfilestr  = [MiscString new];        //for storing method definitions
  159.   returnstr = [MiscString new];        //intermediate string, used for making 
  160.                                     //the return value
  161.  
  162.   iName     = [MiscString new];      //strings to hold filenames
  163.   mName     = [MiscString new];
  164.   hName     = [MiscString new];
  165.  
  166.                                        //for "caseSensitive:(BOOL)sense[YES]":
  167.   defaultList   = [List new];        //for storing the default values (eg "YES")
  168.   paramList     = [List new];        //for storing the parameter name (eg "caseSensitive:")
  169.   paramvalList  = [List new];        //for storing the variable name  (eg "sense")
  170.   paramtypeList = [List new];        //for storing whole parameter w/ typecasts 
  171.                                      //  (eg "caseSensitive:(BOOL)sense")
  172.   
  173.  
  174.   // These are defaults
  175.   infile   = stdin;
  176.   houtfile = stdout;
  177.   moutfile = stdout;
  178.   append   = NO;
  179.  
  180.   re_set_syntax(RE_NO_BK_PARENS);
  181.  
  182.   // Process arguments
  183.   while ((ch = getopt(argc, argv, "Ham:h:f:i:e:")) != EOF)
  184.       switch((char)ch) {
  185.         case '?':
  186.               usage(argv[0]);
  187.         case 'H':
  188.             dohelp(argv[0]);
  189.             freestuff();
  190.             exit(0);
  191.         case 'a':
  192.             append = YES;
  193.             break;
  194.         case 'i':
  195.             [iName setStringValue:optarg];
  196.             break;
  197.         case 'm':
  198.             [mName setStringValue:optarg];
  199.             [mName addExtensionIfNeeded:"m"];
  200.             break;
  201.         case 'h':
  202.             [hName setStringValue:optarg];
  203.             [hName addExtensionIfNeeded:"h"];
  204.             break;
  205.         case 'f':
  206.             [hName setStringValue:optarg];
  207.             [hName cat:".h"];
  208.             [mName setStringValue:optarg];
  209.             [mName cat:".m"];
  210.             break;
  211.         case 'e':
  212.             if (index(optarg,'h')) outputFinalHeader = NO;
  213.             if (index(optarg,'m')) outputFinalMethod = NO;
  214.             break;
  215.         default:
  216.             usage(argv[0]);
  217.             exit(2);
  218.     }
  219.   
  220.  
  221.   if ([iName length]) {
  222.     infile = fopen([iName stringValue], "r+");
  223.     if (!infile) {
  224.         fprintf(stderr,"ERROR: could not open '%s'\n",[iName stringValue]);
  225.         freestuff();
  226.         exit(1);
  227.     }
  228.   }
  229.  
  230.   if ([hName length]) {
  231.       houtfile = fopen([hName stringValue], (append)? "a+":"w+");
  232.     if (!houtfile) {
  233.         fprintf(stderr,"ERROR: could not open '%s'\n",[hName stringValue]);
  234.         freestuff();
  235.         exit(1);
  236.     }
  237.   }
  238.  
  239.   if ([mName length]) {
  240.       moutfile = fopen([mName stringValue], (append)? "a+":"w+");
  241.     if (!moutfile) {
  242.         fprintf(stderr,"ERROR: could not open '%s'\n",[mName stringValue]);
  243.         freestuff();
  244.         exit(1);
  245.     }
  246.   }
  247.   
  248.   // Repeat for every method read in... the || [mainstr length] is there
  249.   // if the last line of input does not have a newline at the end -
  250.   // this loop only terminates if at EOF *and* no string was read in.
  251.   
  252.   while ([mainstr fgets:infile keepNewline:NO] != EOF || [mainstr length]) {
  253.     if ([mainstr length] < 5) continue;  //break out if a blank line
  254.     numparam = 0;
  255.     numposs = 1;
  256.     startposs = 0;
  257.     
  258.     // Isolate each parameter and process it - the regular expression is
  259.     // what expects to see a cast for each variable.
  260.     
  261.     for (i=0;(spot2 = [mainstr spotOfRegex:"[^\t\n :]+[:][(][^)]*[)][^ \t\n[]+([[][^]]*[]][^ \t\n]*)?"
  262.                                occurrenceNum:i length:&len]) >= 0;i++)
  263.     {  
  264.       // The above parsing may not have worked if the method return type had
  265.       // spaces in it, such as (const char *).  This checks to see if that
  266.       // happened and sets 'spot2' and 'len' to the corrected values.
  267.  
  268.       if ((i == 0) && ((spot3 = [mainstr spotOfRegex:"[(][^)]*[)]"]) < spot2)) {
  269.         len += (spot2-spot3);
  270.         spot2 = spot3;
  271.       }
  272.       
  273.       // Now get the whole parameter in a string.
  274.       tmp = [mainstr midFrom:spot2 length:len];
  275.       
  276.       // Add the parameter name (sans type and variable name) to its list.
  277.       // If the method has a return type, this must be stripped out too -
  278.       // thus the check on the first character.
  279.  
  280.       if ([tmp charAt:0] == '(') {
  281.         [paramList addObject:[tmp midFrom:[tmp spotOf:')']+1 to:[tmp spotOf:':']]];
  282.       }
  283.       else {
  284.         [paramList addObject:[tmp midFrom:0 to:[tmp spotOf:':']]];
  285.       }
  286.       
  287.       numparam++;
  288.       if (![tmp endcmp:"]"]) {                // It is an optional param
  289.         id tmp2 = [tmp midFrom:[tmp spotOf:':'] to:[tmp length]];
  290.         int spot = [tmp spotOf:':'] + [tmp2 spotOf:'['];        // Find beginning of default value
  291.         isOptArray[i] = YES;
  292.         
  293.         // Get the whole parameter minus default value and add it to list
  294.         [paramtypeList addObject:[tmp midFrom:0 to:spot-1]];
  295.         
  296.         // Get the default value and add it to its list
  297.         [defaultList addObject:[tmp midFrom:spot+1 to:[tmp length]-2]];
  298.  
  299.         // Get the variable name and add it to its list
  300.         //[paramvalList addObject:[tmp midFrom:[tmp rspotOf:')']+1 to:spot-1]];
  301.     [paramvalList addObject:[tmp2 midFrom:[tmp2 spotOf:')']+1 to:[tmp2 spotOf:'[']-1]];
  302.     [tmp2 free];
  303.         
  304.         // If the first parameter is optional, it is handled much differently.
  305.         // Instead of doubling the number of potential methods, only one more
  306.         // is added.  This is handled by starting the second main loop at -1
  307.         // instead of 0 (startposs).  Also, since in the rest of the methods
  308.         // the first parameter must be there, its slot in the isOptArray should
  309.         // be NO, as it really is not optional.  'specstring' holds the header
  310.         // for the extra method incurred -- which is just the name (and return
  311.         // type if there) of the parameter minus the colon.  'specstring' will
  312.         // be used only in the -1th iteration of the second main loop.
  313.  
  314.         if (i==0) {
  315.           [specstring setStringValue:[[tmp left:[tmp spotOf:':']] stringValueAndFree]];
  316.           [specstring insert:" "];
  317.           startposs = -1;
  318.           isOptArray[0] = NO;
  319.         }
  320.         else {     // Not the first param, so double # of possible methods.
  321.           numposs *= 2;
  322.         }
  323.  
  324.         // Free tmp here... under the else below, it is added to one of the lists
  325.         // and is therefore freed then.
  326.         [tmp free]; 
  327.       }
  328.       else {  // Not an optional parameter, doesn't have [XXX] at end
  329.         isOptArray[i] = NO;
  330.         [defaultList addObject:[MiscString new]];   // Add a dummy string to keep place
  331.         [paramtypeList addObject:tmp];
  332.         [paramvalList addObject:[tmp midFrom:[tmp rspotOf:')']+1 to:[tmp length]-1]];
  333.       }
  334.  
  335.  
  336.     } //done parsing the input line
  337.  
  338. /*     //debugging with parsing...
  339.     for (i=0;k<numparam;k++) {
  340.       printf("Full param: %s\n",[[paramtypeList objectAt:i] stringValue]);
  341.       printf("Optional? %s\n",(isOptArray[i])? "YES":"NO");
  342.       printf("Default: '%s'\n",[[defaultList objectAt:i] stringValue]);
  343.       printf("Value: %s\n",[[paramvalList objectAt:i] stringValue]);
  344.       printf("Name: %s\n",[[paramList objectAt:i] stringValue]);
  345.     }
  346. */
  347.     // Okay, now output stuff.  There should be numposs methods to do.
  348.     // 'startposs' is -1 if the first param is optional and the one
  349.     // extra method is there to do, 0 normally.
  350.  
  351.     for (i=startposs;i<numposs;i++) {
  352.       [headstring setStringValue:[[mainstr wordNum:0] stringValueAndFree]]; //"-" or "+"
  353.       [returnstr setStringValue:"{ return [self"];
  354.       thenum = 1;
  355.       
  356.       // Build up the header and return for each method.  hfilestr holds
  357.       // the method headers, mfilestr holds the method name, and returnstr
  358.       // holds the return value which gets added to mfilestr at the end of
  359.       // the inner loop.
  360.       
  361.       for (j=0;j<numparam;j++) {
  362.       
  363.         // Add the method name to the return value.  The argument will
  364.         // either be the variable name or the default
  365.         [returnstr addChar:' '];
  366.         [returnstr concatenate:[paramList objectAt:j]];
  367.         
  368.         // Check to see if the current parameter deserves to be added
  369.         // in this pass.  If it does, then add it to the header,
  370.         // and add the variable name to returnstr.  Otherwise, just add
  371.         // the default value to returnstr.
  372.         // The special case of i==-1 should not do this part - that is handled
  373.         // elsewhere.  If the current parameter is not optional then it should
  374.         // always be added in.
  375.         // Otherwise, the third condition determines whether a parameter is
  376.         // included in this pass.  'thenum' starts as 1 every iteration of i,
  377.         // meaning the value of that condition for the first parameter will
  378.         // be 0, then 1, then 0, then 1... as i goes up.  'thenum' gets doubled
  379.         // each optional parameter, so for the second optional parameter the
  380.         // condition will be 0, 0, 1, 1, 0, 0, 1, 1... as i goes up.  The third
  381.         // will be 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, ... and so on.  Since i represents
  382.         // the total number of combinations, this will account for every possible
  383.         // combination.
  384.         
  385.         if ((i>=0) && (!(isOptArray[j]) || ((i % (thenum*2)) >= thenum))) {
  386.           [headstring addChar:' '];
  387.           [headstring concatenate:[paramtypeList objectAt:j]];
  388.           [returnstr concatenate:[paramvalList objectAt:j]]; //the variable name
  389.          }
  390.        else {
  391.           [returnstr concatenate:[defaultList objectAt:j]];  //the default value
  392.          }
  393.         if (isOptArray[j]) thenum *= 2;
  394.        }
  395.       
  396.       // Special case handling for i==-1.  Only the else portion of the above
  397.       // will be executed, so only the returnstr is done.  specstring, set in
  398.       // the parsing loop, holds the header needed by hfilestr and mfilestr.
  399.       // So, just add it to them.
  400.       
  401.       if (i==-1) {
  402.         [headstring concatenate:specstring];
  403.       }
  404.       
  405.       [returnstr cat:"]; }"];
  406.  
  407.       if ((i <(numposs-1)) || outputFinalHeader) {
  408.           [hfilestr concatenate:headstring];
  409.           [hfilestr cat:";\n"];
  410.       }
  411.       if ((i < (numposs-1)) || outputFinalMethod) {
  412.           [mfilestr concatenate:headstring];
  413.       }
  414.       // If we are at the last possible method (i == numposs-1) we don't want
  415.       // to put a method body in - it would just call itself and be an infinite
  416.       // loop.  I just decided to put in a blank method body - you will at least
  417.       // get a warning from the compiler if you forget to take this out or implement
  418.       // it.  At least if you use -Wall.
  419.       // Otherwise, returnstr holds the entire method body, so add that to mfilestr.
  420.       
  421.       if (i < (numposs-1))  //don't put in return for richest method
  422.         [mfilestr catFromFormat:"\n%s\n\n",[returnstr stringValue]];
  423.       else if (outputFinalMethod)
  424.         [mfilestr cat:"\n{\n}\n\n"];
  425.     }
  426.     
  427.     [hfilestr addChar:'\n'];        // Leave a blank line between sets of headers
  428.     [mfilestr cat:"\n\n\n"];        // Leave three blank lines between sets of 
  429.                                     // convenience methods.
  430.  
  431.     // Empty out all the lists to start over with the next input line
  432.     [defaultList freeObjects];
  433.     [paramList freeObjects];
  434.     [paramvalList freeObjects];
  435.     [paramtypeList freeObjects];
  436.    }
  437.   
  438.   // Output the info... yeah, it only gets output at the end instead of
  439.   // as it's processed, but that's the price to pay if you want headers
  440.   // grouped together instead of interspersed with the actual methods
  441.   // when both outputs go to stdout.
  442.   
  443.   fprintf(houtfile,"%s\n\n",[hfilestr stringValue]);
  444.   fprintf(moutfile,"%s\n",[mfilestr stringValue]);
  445.  
  446.   // And clean up...
  447.   
  448.   if ([hName length]) fclose(houtfile);
  449.   if ([mName length]) fclose(moutfile);
  450.   if ([iName length]) fclose(infile);
  451.   freestuff();
  452.   
  453. }
  454.  
  455. void freestuff()
  456. {
  457.   [hName free];
  458.   [mName free];
  459.   [iName free];
  460.   [specstring free];
  461.   [headstring free];
  462.   [returnstr free];
  463.   [mfilestr free];
  464.   [hfilestr free];
  465.   [[defaultList freeObjects] free];
  466.   [[paramList freeObjects] free];
  467.   [[paramvalList freeObjects] free];
  468.   [[paramtypeList freeObjects] free];
  469.   [mainstr free];
  470. }
  471.  
  472. void usage(char *progname)
  473. {
  474.   fprintf(stderr,"\nusage: %s [-Ha] [-i inputfilename]\n",progname);
  475.   fprintf(stderr,"               [-h hfilename -m mfilename | -f basefilename]\n");
  476.   fprintf(stderr,"               [-e {h|m|hm}]\n\n");
  477.   fprintf(stderr,"      %s -H for more verbose help.\n\n",progname);
  478.   freestuff();
  479.   exit(2);
  480. }
  481.  
  482.  
  483. void dohelp(char *progname)
  484. {
  485.   printf("usage: %s [-Ha] [-i filename] [-e {h|m|hm}]\n",progname);
  486.   printf("          [-h hfilename -m mfilename | -f basefilename]\n\n");
  487.   printf("     This program takes method headers with specified default values for\n");
  488.   printf("    optional parameters, and outputs both headers and methods for\n");
  489.   printf("    all possible degenerate (convenience) methods.\n");
  490.   printf("     Reads from stdin if infile not specified, and outputs headers\n");
  491.   printf("    and/or methods to stdout if destination is not specified.\n\n");
  492.   printf("        -H     this help\n");
  493.   printf("        -a     append to named output files; don't overwrite\n");
  494.   printf("        -i     take input from named file\n");
  495.   printf("        -h/-m  output headers/methods to named file.  Adds '.h'/'.m' to\n");
  496.   printf("                  filename if not there already.\n");
  497.   printf("        -f   same as -h basefilename.h -m basefilename.m\n");
  498.   printf("        -e {h|m|hm} Excludes final {header|method|both} from being output.\n");
  499.   printf("\n");
  500.   printf("  INPUT SYNTAX: (one line per method)\n");
  501.   printf("      - (int)spotOf:(char)aChar caseSensitive:(BOOL)sense[YES]\n");
  502.   printf("       ^   ^          ^__________________________^       ^\n");
  503.   printf("       |   |                  |                          |__NO space\n");
  504.   printf("     SPACE |     CAST each parameter--even if id\n");
  505.   printf("          Method cast is optional\n");
  506.  
  507. }
  508.